package com.eblacorp.archive.dao.fn.oracle;

import java.beans.IntrospectionException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.eblacorp.archive.dao.fn.GenericFnDao;
import com.eblacorp.archive.domain.fn.Archivable;
import com.eblacorp.archive.exceptions.NoAttachedFnDocument;
import com.eblacorp.archive.exceptions.NotFnDocumentException;
import com.eblacorp.archive.exceptions.NullValueException;
import com.eblacorp.archive.util.ce.CeConnectionUtil;
import com.eblacorp.archive.util.ce.ContentEngineUtil;
import com.filenet.api.collection.IndependentObjectSet;
import com.filenet.api.collection.RepositoryRowSet;
import com.filenet.api.constants.CheckinType;
import com.filenet.api.constants.RefreshMode;
import com.filenet.api.core.Document;
import com.filenet.api.query.SearchSQL;
import com.filenet.api.query.SearchScope;
import com.filenet.api.util.Id;

public class GenericFnDaoImpl <T extends Archivable> implements GenericFnDao<T>{

	/**
	 * 
	 */
	private static final long serialVersionUID = 8863333989476538632L;
	final static Log log = LogFactory.getLog(GenericFnDaoImpl.class);
	
	private CeConnectionUtil ceConnectionUtil;
	private ContentEngineUtil<T> contentEngineUtil;
	
	private Class<T> persistentClass;
	private String persistentClassName;
	private String persistentClassSimpleName;
	

	@SuppressWarnings("unchecked")
	public GenericFnDaoImpl() {
		 persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		 persistentClassName = persistentClass.getName();
		 persistentClassSimpleName = persistentClass.getSimpleName();
		
	}
	
	@PostConstruct
	public void init() throws NotFnDocumentException{
		contentEngineUtil.setPersistentClass(persistentClass);
	}


	@Override
	public List<T> getByExample(final T t,Integer startPage, Integer pageSize, boolean withDocument)
			throws IllegalArgumentException, NotFnDocumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, InstantiationException {
		List<T> objects = null;
		
		SearchSQL searchSQL = contentEngineUtil.getSeatchSQL(t, pageSize, null, null, null);
		SearchScope searchScope = new SearchScope(ceConnectionUtil.getObjectStore());
		if(withDocument){
			IndependentObjectSet independentObjectSet = searchScope.fetchObjects(searchSQL, pageSize, null, null);	
			objects = contentEngineUtil.getObjects(independentObjectSet);
		}else{
			RepositoryRowSet repositoryRowSet = searchScope.fetchRows(searchSQL, pageSize, null, null);	
			objects = contentEngineUtil.getObjects(repositoryRowSet);
		}
			
		return objects;
	}

	@Override
	public T getById(Id id, boolean withContent) throws IllegalArgumentException, InstantiationException, IllegalAccessException, IntrospectionException, InvocationTargetException {
		Document document = contentEngineUtil.fetchDocumentById(ceConnectionUtil.getObjectStore(), id, withContent);
		return contentEngineUtil.getObject(document);
	}
	
	@Override
	public T create(T t, File file, CheckinType checkinType) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, NullValueException, IOException {
		Validate.notNull(checkinType);
		Validate.notNull(t);
		Document document = contentEngineUtil.createDocument(ceConnectionUtil.getObjectStore(), t);
		if(file != null  && document != null){
			contentEngineUtil.addContent(document, file);
		}
		contentEngineUtil.checkinDocument(document, checkinType, RefreshMode.REFRESH);
		t.setDocument(document);
		return t;
	}

	@Override
	public T update(T t, File file, CheckinType checkinType) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, NullValueException, IOException {
		Validate.notNull(t);
		Document document = null;
		if(t.getDocument() == null){
			document = contentEngineUtil.fetchDocumentById(ceConnectionUtil.getObjectStore(), t.getDocumentId(), false);
			t.setDocument(document);
		}else{
			document = t.getDocument();
		}
		if(checkinType != null){
			document = contentEngineUtil.checkoutDocument(document);
			t.setDocument(document);
			document = contentEngineUtil.updateDocument(ceConnectionUtil.getObjectStore(), t);
			if(file != null  && document != null){
				Validate.notNull(checkinType);
				document = contentEngineUtil.addContent(document, file);
			}
			contentEngineUtil.checkinDocument(document, checkinType, RefreshMode.NO_REFRESH);
		} else{
			document = contentEngineUtil.updateDocument(ceConnectionUtil.getObjectStore(), t);
			contentEngineUtil.saveDocument(document, RefreshMode.NO_REFRESH);
		}
		t.setDocument(document);
		return t;
	}
	
	@Override
	public void delete(T t, boolean allVersion) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, NullValueException {
		Document document = null;
		if(t.getDocument() == null){
			document = contentEngineUtil.fetchDocumentById(ceConnectionUtil.getObjectStore(), t.getDocumentId(), false);
		}else{
			document = t.getDocument();
		}
		contentEngineUtil.delete(document, allVersion);
	}
	
	@Override
	public byte[] download(T t) throws NoAttachedFnDocument, IOException {
		if(t.getDocument() == null){
			throw new NoAttachedFnDocument();
		}
		
		return contentEngineUtil.download(t.getDocument(), ceConnectionUtil.getObjectStore());
	}
	
	@Override
	public String getWcmJavaViewerUrl(T t) {
		return ceConnectionUtil.getWcmJavaViewerUrl(t.getDocumentId());
	}

	public ContentEngineUtil<T> getContentEngineUtil() {
		return contentEngineUtil;
	}

	public void setContentEngineUtil(ContentEngineUtil<T> contentEngineUtil) {
		this.contentEngineUtil = contentEngineUtil;
	}

	public CeConnectionUtil getCeConnectionUtil() {
		return ceConnectionUtil;
	}

	public void setCeConnectionUtil(CeConnectionUtil ceConnectionUtil) {
		this.ceConnectionUtil = ceConnectionUtil;
	}
}
